// LUA Stripper.cpp : Scans the LUA output of the BankItems addon for Warcraft
//1.12.1 for the items and count and dumps them to a cleaner text file.

//Current known bugs:
//Can't ID greens with random enchantments
//BankItems doesn't record what's in the bag's of the character, so this doesn't either

#include "stdafx.h"
#include <fstream>
#include <iostream>
#include <time.h>

using namespace std;

const int nameLength = 128; //String length, change if name needs to be bigger later

struct Item {
	int amount; //0 for empty, 1-? for stack size
	char name[nameLength]; //Name of item
	int itemID; //WoW based itemID number based on a provided list
	char RGB[6]; //Hex RGB color of the item's link
};

struct Banker {
	char name[12]; //Game limits names to 12 characters
	int start; //First item in main array
	int end; //Last item in main array
};

struct itemID{
	int ID; //ID number
	char name[nameLength]; //Name of Item
};

Item mainItem[1088]; //create main storage for information. 1088 is the max bank slots with 1 account/8 alts
Banker bankers[8]; //Max 8 alts per account
itemID mainItemID[13793]; //Item Database isn't going to change without an expantion change


bool bSort(int startPos, int endPos);
void strip(ifstream &source, int * numItem, int * numBanker);
void duplicate(int start, int end);
void write(_TCHAR * output, int numItem, int numBanker);
void itemGather(ifstream &list);
void IDmatch(int numItem);


//command arguments: LUA source, TXT itemId source, output HTML file
int _tmain(int argc, _TCHAR* argv[]) 
{
	if (argc < 4) //Notify user how to provide the needed files to the program
	{
		cout << "BankItem LUA Stripper: Scans the output from BankItems 10900 for bank contents\n";
		cout << "extracts the item name & count, find's the WoW itemID, and creates an HTML\n";
		cout << "file that provides the old.wowhead.com mouseover information sorted by\ncharacter.";
		cout << "\nUsage: \"LUA Stripper\" *lua file* *itemID file* *output.html*\n";
		cout << "Example: \"LUA Stripper\" BankItem.lua itemlist.txt contents.html";
		return 99; //close program with 99
	}
	ifstream bank, itemList; 
	bank.open(argv[1]); //Open the 1nd command line argument as the input file
	if (bank.fail())
	{
		cout << "Can't locate LUA file as provided.\n";
		return 1; //close program with error 1
	}
	itemList.open(argv[2]); //Open the 2nd command line argument as the input file
	if (itemList.fail())
	{
		cout << "Can't locate itemID text file as provided.\n";
		return 2; //close program with error 2
	}
	itemGather(itemList); //gather from the file into memory
	itemList.close(); // release the file.

	int numItem = 0, //Total number of Items
		numBanker = 0; //Total number of Bankers
	
	bool match;

	for (int i = 0; i < 1088; i++) //initialize the list
	{
		mainItem[i].amount = 0;
		mainItem[i].itemID = 0;
	}

	for (int i = 0; i < 8; i++) //initialize bankers
	{
		bankers[i].start = 0; bankers[i].end = 0;
	}
	
	//Strip all the LUA coding out of the input file and save it to memory
	strip(bank, &numItem, &numBanker);
	bank.close(); //Close Bank file

	//Check for duplicate names in memory and combine their ammounts for each Banker
	for (int i = 0; i < numBanker; i++)
		duplicate(bankers[i].start,bankers[i].end);

	//Find IDs for the items currently available.
	IDmatch(numItem);

	//Sort the items in the list into alpha order within each banker's items, using Bubble Sort 
	for (int i = 0; i < numBanker; i++)
	{
		match = bSort(bankers[i].start,bankers[i].end);
		while (match) match = bSort(bankers[i].start,bankers[i].end);
	}

	// Now that the main gathering is all finished, output the array to a HTML file via the dump stream
	write (argv[3], numItem, numBanker);
	return 0;
}

bool bSort(int startPos, int endPos) //Bubble Sort: Returns True if sorting was done, returns false is nothing changed
{
	bool higher,checked, //string checking
		result = true; //Assume sorting is done, toggle if anything moves
	int start = startPos, end = endPos, next = startPos + 1, charTrack = 0,
		tempAmount, tempID; // for swaping
	char tempName[nameLength], tempRGB[6]; // for swaping
		result = false; //Assume loop will find nothing, causing the loop in main to exit
		for (start; start < end -1; start++)
		{
			next = start + 1;
			higher = false; //reset on each loop
			checked = false; 
			while (!checked && charTrack < nameLength) //If current has a higher value than current+1, mark to be swapped.
			{
				if (mainItem[start].name[charTrack] != mainItem[next].name[charTrack]) // Wait for a difference
				{
					checked = true; //Note a difference was found
					if (mainItem[start].name[charTrack] > mainItem[next].name[charTrack]) higher = true; //See if it's higher and record
				}
				charTrack++;
			}
			charTrack = 0; //Reseting
			if (higher) //Start swap if higher
			{
				tempAmount = mainItem[start].amount; tempID = mainItem[start].itemID; //store ints
				while (charTrack < nameLength)
				{tempName[charTrack] = mainItem[start].name[charTrack]; charTrack++;} // store name
				charTrack = 0; //reseting
				//store RGB
				tempRGB[0] = mainItem[start].RGB[0]; tempRGB[1] = mainItem[start].RGB[1]; tempRGB[2] = mainItem[start].RGB[2];
				tempRGB[3] = mainItem[start].RGB[3]; tempRGB[4] = mainItem[start].RGB[4]; tempRGB[5] = mainItem[start].RGB[5];

				mainItem[start].amount = mainItem[next].amount; mainItem[start].itemID = mainItem[next].itemID; //swap ints
				while (charTrack < nameLength)
				{mainItem[start].name[charTrack] = mainItem[next].name[charTrack]; charTrack++;} //swap name
				charTrack = 0; //reseting
				//swap RGB
				mainItem[start].RGB[0] = mainItem[next].RGB[0]; mainItem[start].RGB[1] = mainItem[next].RGB[1]; mainItem[start].RGB[2] = mainItem[next].RGB[2];
				mainItem[start].RGB[3] = mainItem[next].RGB[3]; mainItem[start].RGB[4] = mainItem[next].RGB[4]; mainItem[start].RGB[5] = mainItem[next].RGB[5];

				mainItem[next].amount = tempAmount; mainItem[next].itemID = tempID; //restore ints
				while (charTrack < nameLength)
				{mainItem[next].name[charTrack] = tempName[charTrack]; charTrack++;} //restore name
				charTrack = 0; //reseting
				//restore RGB
				mainItem[next].RGB[0] = tempRGB[0]; mainItem[next].RGB[1] = tempRGB[1]; mainItem[next].RGB[2] = tempRGB[2];
				mainItem[next].RGB[3] = tempRGB[3]; mainItem[next].RGB[4] = tempRGB[4]; mainItem[next].RGB[5] = tempRGB[5];

			}
			if (!result && higher) result = true; //toggle result as true if a swap is done.
		}
	return result;
}

void strip(ifstream &source, int * numItem, int * numBanker) //Logic that locates the parts of the LUA file we want and records them to memory
{
	char test, checkName[20]; //temp char for parsing the file, temp space for character names.
	int temp = 0, //temp variable
		curItem = 0, //Current Item in the list
		curBanker = 0, //current banker
		charTrack = 0; //string position
	
	bool match, quote,
		bagFound = false, //Need to spot "Bag" tags and prepare for them
		count = false; //Need to note when "count" happens to filter out used but empty bags

	test = source.get(); //The LUA file this reads from is full of garbage
	while (source)  //main gathering loop
	{
		test = source.get();
		//The size of the stack comes before the name, so checking for that first just to make putting things in the array easier
		if (test == '=') //The size of stacks are after a "= " string
		{
			source.get(); //Get to whitespace
			test = source.peek(); //Looking for a digit
			temp = test;
			if ((temp >= 48) && (temp <= 57) && count) //If the next character is a number after a "count" tag, save to the Item.ammount space.
			{
				source >> mainItem[curItem].amount;
				count = false; // clear flag
			}
		}

		if (test == '[') //strings we want start after a [
		{
			test = source.get();
			if (test != '\"') //Items don't have double quotes in them, character names will.
			{
				temp = test; //Capture the character for testing
				while ((temp >= 48) && (temp <= 57)) //Checking for digits, only want text so we push until we're not on a digit
				{
					test = source.get();
					temp = test;
				}
				if(test != ']') //Capture string to file if it hasn't been shown to be a number by the above
				{
					if (bagFound && mainItem[curItem].amount == 0) //Current item as it's a used bag
					{
						bagFound = false;
						while (test != ']')
							test = source.get();
					}
					else //otherwise record it and advance the numItems.
					{
						while ((test != ']') && (charTrack < nameLength - 1)) //Watch for ] or name's length - 1
						{
							mainItem[curItem].name[charTrack] = test;
							charTrack++;
							test = source.get();
						}
						//Put eol at the end of the string
						mainItem[curItem].name[charTrack] = '\n';
						//Reset CharTrack for next use
						charTrack = 0;
						//Now that the string is collected, increment current & total array number and current banker.end point
						bankers[curBanker].end++; *numItem = *numItem + 1; curItem++;
					}
				}
			}
			else //Looking for character names, they will have | in them.
			{
				match = false; // toggle on a |
				quote = false; // toggle on a "
				while (charTrack < 20 && !match && !quote) //stop on match or quote toggle
				{
					test = source.get();
					checkName[charTrack] = test; //Gather next character
					if (test == '|') match = true; //If | is found, toggle match for collection
					if (test == '\"') quote = true; //If " is found, toggle quote for quiting loop
					charTrack++;
				}
				charTrack = 0; // Reset
				if (match) //If we found a | then it needs to be saved. 
				{
					*numBanker = *numBanker + 1; curBanker = *numBanker - 1;//Increase number of banks
					while (charTrack < 12) //Keep going until | or max name length (12) is hit
					{
						if (checkName[charTrack] != '|')
						{
							bankers[curBanker].name[charTrack] = checkName[charTrack]; 
							charTrack++;
						}
						else
						{
							bankers[curBanker].name[charTrack] = '\n'; //Cap name
							charTrack = 12; //Force the loop to exit
						}
					}
					charTrack = 0; // Reset
					bankers[curBanker].start = *numItem; bankers[curBanker].end = *numItem; //Set start & end points for bank
				}
				if (quote)
					//looking for "Bag" as we will want to ignore the next item found as it's a used bag
					if (checkName[0] == 'B' && checkName[1] == 'a' && checkName[2] == 'g')
						bagFound = true;
					//looking for "count" to fill in the next amount but still able to ignore used bags
					else if (checkName[0] = 'c' && checkName[1] == 'o' && checkName[2] == 'u' && checkName[3] == 'n' && checkName[4] == 't')
						count = true;
				while (charTrack < 20) //clear the string
				{
					checkName[charTrack] = '\n';
					charTrack++;
				}
				charTrack = 0; //Reset
			}
		}
		if (test == '|')  //Looking for RGB information
		{
			test = source.get(); //need to check for 'c'
			if (test == 'c')
			{
				source.get(); source.get(); test = source.get(); //Jump 3 characters
				while (charTrack < 6)//record the next 6 characters to mainItem.RGB
				{
					mainItem[curItem].RGB[charTrack] = test;
					charTrack++;
					test = source.get();
				}
				charTrack = 0; //reset
			}
		}
	}
}

void duplicate(int start, int end) //Finds duplicate entries in the list and blanks/0s out the extras and combines the amounts
{
	int count = 0, //temp variable
		curItem = start, //Current Item in the list
		charTrack = 0; //string position
	
	bool match; 
	for (curItem; curItem < end; curItem++)
	{
		count = curItem + 1;
		for (count; count < end; count++) // Start at curItem + 1, go to end of used array
		{
			match = true; //Assuming a match, false on failure
			//fail loop if character match fails, stop if nameLength reached or eol
			while (match && charTrack != nameLength && mainItem[curItem].name[charTrack] != '\n') 
			{
				if (mainItem[curItem].name[charTrack] != mainItem[count].name[charTrack])
					match = false;
				else
					charTrack++;
			}
			//reset charTrack
			charTrack = 0;
			//If a match was found, combine the amount and blank out the later entry. Ignoring text
			if (match)
			{
				mainItem[curItem].amount += mainItem[count].amount;
				mainItem[count].amount = 0;
				mainItem[count].itemID = 0;
			}
		}
	}
}

void write(_TCHAR * output, int numItem, int numBanker) //write HTML file with given information that can link back to old.wowhead.com for mouseover
{
	int count = 0, //temp variable
	curItem = 0, //Current Item in the list
	curBanker = 0, //current banker
	charTrack = 0; //string position
	
	ofstream dump; //("C:/Vanilla WoW/WTF/Account/GUILDEBANK/SavedVariables/guildebank.txt");
	dump.open(output);
	//unelegenently dump html header first'
	dump << "<html xmlns=\"http://www.w3.org/1999/xhtml\">\n";
	dump << "<head>\n";
	dump << "<meta http-equiv=\"Content-Type\" content=\"text/html;charset=UTF-8\"/>\n";
	dump << "<script type=\"text/javascript\" src=\"http://static.wowhead.com/widgets/power.js\"></script><style type=\"text/css\">\n";
	dump << "body { padding: 50px 15px 15px 15px; margin: 0; font-family: Arial; color: white; background: black }\n";
	dump << "a { cursor: pointer; outline: none }\n";
	dump << "h2 { font-size: 20px; margin-top: 0; padding-top: 0; line-height: 1.5em }\n";
	dump << "h2 small { font-size: 13px; font-weight: normal }\n";
	dump << "</style>\n" << "</head>\n" << "<body>\n";
	//Simply getting the current time/date to stamp the file with
	char dateStr [9];
    char timeStr [9];
    _strdate_s(dateStr); 
    _strtime_s(timeStr);
	dump << "Last Updated: " << dateStr << " @ " << timeStr << " EDT (-5 GMT) \n<p>";
	//Start HTML table tagging
	dump << "<table>\n";
	for (curItem; curItem < numItem; curItem++)
	{
		//Output current Banker's name when curItem == banker.start
		if (curItem == bankers[curBanker].start)
		{
			//Start table row & cell, also color font white.
			dump << "<tr><td><font color=#FFFFFF>";
			while (charTrack < 12 && bankers[curBanker].name[charTrack] != '\n') //Output name string
			{
				dump << bankers[curBanker].name[charTrack];
				charTrack++;
			}
			charTrack = 0; // Reset
			//close font & row/cell tags
			dump << "</font></td></tr>\n";
			curBanker++; //Increment to next banker to catch it's start
		}
		//Ignore amounts of 0 as they were combined earlier and shouldn't go to file
		if (mainItem[curItem].amount != 0)
		{
			//create HTML row & cell tag
			dump << "<tr><td>";
			// Write stack size, mark end of cell
			dump << mainItem[curItem].amount << "</td>";
			// html cell & link tag to old.wowhead.com using itemID
			dump << "<td><a href=\"http://old.wowhead.com/item=" << mainItem[curItem].itemID << "\">";
			//create color
			dump << "<font color=#" << mainItem[curItem].RGB[0] << mainItem[curItem].RGB[1] << mainItem[curItem].RGB[2] << mainItem[curItem].RGB[3] << mainItem[curItem].RGB[4] << mainItem[curItem].RGB[5] << '>';
			//Write out item name to file.
			while (mainItem[curItem].name[charTrack] != '\n') //Output the name string until eol
			{
				dump << mainItem[curItem].name[charTrack];
				charTrack++;
			}
			charTrack = 0;  //Reset after use
			//close HTML tags
			dump << "</font></a></td></tr>\n";
		}
	}
	//close up HTML
	dump << "</table>\n</body>\n</html>";
	dump.close(); //close file
}

void itemGather(ifstream &list) //The file is specificly formatted and easy to pull from. It's number \t Name of item\n
{
	int charTrack = 0;
	char test;
	for (int i = 0; i < 13793; i++)
	{
		list >> mainItemID[i].ID; //get ID number
		test = list.get(); //move past whitespace
		while (test != '\n' && charTrack < nameLength)
		{
			test = list.get();
			mainItemID[i].name[charTrack] = test;
			charTrack++;
		}
		mainItemID[i].name[charTrack] = '\n'; //cap strings
		charTrack = 0; //reset
	}
}

void IDmatch(int numItem) //Match names of items in memory from mainItems to mainItemID and record the itemID to mainItems
{
	int count = 0, //temp variable
		curItem = 0, //Current Item in the list
		charTrack = 0; //string position
	
	bool match, //Watching for matched strings
		record; //Used to exit the main while loop when the item ID is found

	for (curItem; curItem < numItem; curItem++)
		if (mainItem[curItem].amount != 0) //Ignore amounts of 0 as they were combined earlier
		{
			record = false; //Toggle when ID is found in recorded to stop loop, leave true if found
			count = 0; //Reset count
			while (!record && count < 13793) //Just incase we can't find the item on the list
			{
				match = true; //Toggle when match is found to stop inner loop & record
				//fail loop if character match fails, stop if nameLength reached or eol
				while (match && charTrack != nameLength && mainItem[curItem].name[charTrack] != '\n') 
				{
					//Comb strings until there's no match, then mark match as false to exit loop
					if (mainItem[curItem].name[charTrack] != mainItemID[count].name[charTrack])
					{
						match = false;
						count++;
					}
					else 
						charTrack++;
				}
				//reset charTrack
				charTrack = 0;
				//if match is still True, record itemID from where count currently is and toggle record.
				if (match)
				{
					mainItem[curItem].itemID = mainItemID[count].ID;
					record = true;
				}
			}
		}
}
